/*
 * Emini Physics Engine Demo source 
 * by Alexander Adensamer
 * 
 * Copyright (c) 2010, Emini Physics - Alexander Adensamer
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met:
 * 
 *  * Redistributions of source code must retain the above 
 *    copyright notice, this list of conditions and the 
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above 
 *    copyright notice, this list of conditions and the following 
 *    disclaimer in the documentation and/or other materials provided 
 *    with the distribution.
 *  * Neither the name of the Emini Physics nor the names of 
 *    its contributors may be used to endorse or promote products 
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */

package com.myapp.balltest.graphics;

import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import at.emini.physics2D.Body;
import at.emini.physics2D.Constraint;
import at.emini.physics2D.Landscape;
import at.emini.physics2D.ParticleEmitter;
import at.emini.physics2D.Shape;
import at.emini.physics2D.Spring;
import at.emini.physics2D.UserData;
import at.emini.physics2D.World;
import at.emini.physics2D.util.FXUtil;
import at.emini.physics2D.util.FXVector;

/**
 * Extension of the physical world
 * adding graphic routines to display the world. <br>
 * 
 * Also some other misc features are added.
 * 
 * @author Alexander Adensamer
 */
public class GraphicsWorld extends World implements SensorListener
{
    /**
     * Flag whether to draw the landscape of the bodies. <br>
     * This can be set to false if the background drawing handles the landscape.
     * See @link {@link JumperCanvas}. 
     */
    private boolean drawLandscape = false; 

    private Body bodyToRemove = null;
    
	//helper points for spring drawing
    private FXVector p1 = new FXVector();
    private FXVector p2 = new FXVector();
        
    //the random number generator for the jitter functionality
    private Random random = new Random();

    public static final Paint renderBodies = new Paint();
    public static final Paint renderStaticBodies = new Paint();
    public static final Paint renderSpring = new Paint();
    public static final Paint renderLandscape = new Paint();
    public static final Paint renderParticles = new Paint();
    static
    {
        
        renderBodies.setAntiAlias(false		);
        renderBodies.setColor    (0xFFFFFFFF);
        renderBodies.setTextSize (20.0f		); //Added by Alen Brkicic on 27.02.2010
        
        renderStaticBodies.setAntiAlias(false);
        renderStaticBodies.setColorFilter(new LightingColorFilter(0xffa0a0a0, 0xff000000));
        
        renderSpring.setColor(0xFFF0F0F0);        

        renderLandscape.setColor(0xFFC0C0C0);        

        renderParticles.setColor(0xFFA0A0A0);        
    }
    
    private int baseGravityFX = 150 * FXUtil.ONE_FX;
    private FXVector newGravity = new FXVector(0, baseGravityFX);
    
    /**
     * Default constructor
     */
    public GraphicsWorld()
    {
        super();
    }
    
    public GraphicsWorld(int baseGravityFX)
    {
        super();
        this.baseGravityFX = baseGravityFX;
    }

    /**
     * Copy Constructor
     * @param world the world to copy
     */
    public GraphicsWorld(World world)
    {        
        super(world);
        
    }

    /**
     * Render the world to the graphics object
     * @param graphics the graphics object
     */
    public void draw(Canvas canvas, int xTranslate, int yTranslate)
    {           
        canvas.translate(-xTranslate, -yTranslate);
        
        //draw the landscape by walking over all segments and drawing the line
        if ( drawLandscape)
        {
            Landscape landscape = getLandscape();
            FXVector startPoint, endPoint;
            for( int i = 0; i < landscape.segmentCount(); i++)
            {        
                startPoint = landscape.startPoint(i);
                endPoint = landscape.endPoint(i);
                canvas.drawLine(startPoint.xFX >> FXUtil.DECIMAL, 
                                startPoint.yFX >> FXUtil.DECIMAL,
                                endPoint.xFX >> FXUtil.DECIMAL, 
                                endPoint.yFX >> FXUtil.DECIMAL,
                                renderLandscape);                
            }
        }
        
        
        //draw the springs
        //by walking over all constraint and checking for springs
        //the springs are drawn as a line from anchor one to anchor two  
        int constraintCount = getConstraintCount();
        Constraint[] constraints = getConstraints();
        
        for(int i = 0; i < constraintCount; i++)
        {    
            if (constraints[i] instanceof Spring)
            {
                Spring spring = (Spring) constraints[i];
                spring.getPoint1(p1);
                spring.getPoint2(p2);
                canvas.drawLine(p1.xFX >> FXUtil.DECIMAL, p1.yFX >> FXUtil.DECIMAL, 
                                p2.xFX >> FXUtil.DECIMAL, p2.yFX >> FXUtil.DECIMAL, 
                                renderSpring);                
            }
        }
        
        

        for( int i = 0; i < getParticleEmitters().size(); i++)
        {            
            ParticleEmitter emitter = (ParticleEmitter) getParticleEmitters().elementAt(i);
            
            int[] xFX = emitter.getXPosFX();
            int[] yFX = emitter.getYPosFX();
            short[] life = emitter.getLifeTimes();
            int size = xFX.length;                
            for( int j = 0; j < size; j++)
            {
                if (life[j] > 0)
                {              
                    canvas.drawCircle(xFX[j]>>FXUtil.DECIMAL, yFX[j]>>FXUtil.DECIMAL, 2, renderParticles);
                    //canvas.drawBitmap(particleImage, xFX[j]>>FXUtil.DECIMAL, yFX[j]>>FXUtil.DECIMAL, renderParticles);                    
                }
            }            
        }
        
        /*
         * Draw the bodies
         * by walking over all bodies that are currently in
         * simulation scope (see World#setSimulationArea())
         * so we walk from getBodyStartIndex() to getBodyEndIndex()
         */        
        Body[] bodies = getBodies();
        int bodyEndIndex = getBodyEndIndex();        
        for(int i = getBodyStartIndex(); i < bodyEndIndex; i++)
        {   
        	drawBody( canvas, bodies[i], xTranslate, yTranslate);
        }
        
        canvas.translate(xTranslate, yTranslate);
    }
   
    
    /**
     * Render a single body to the graphics object. <br>
     * Checks the ShapeSet if an image exists.
     * Checks if the shape is colored and supplies a non-default color (used in @link {@link BilliardSimulationCanvas}
     * Checks whether to draw the contour and/or the fill 
     * @param graphics  the graphics object 
     * @param body the body to draw
     */
    private Matrix matrix = new Matrix();    
    public final void drawBody(Canvas canvas, Body body, int xTranslate, int yTranslate)
    {   
        //check whether body is on screen
        if ( body.getAABBMinXFX() > (canvas.getWidth() + xTranslate ) * FXUtil.ONE_FX || 
             body.getAABBMaxXFX() < xTranslate * FXUtil.ONE_FX ||
             body.getAABBMinYFX() > (canvas.getHeight() + yTranslate ) * FXUtil.ONE_FX || 
             body.getAABBMaxYFX() < yTranslate * FXUtil.ONE_FX )
        {
            return;
        }
        
        Shape shape = body.shape();
        
        //Bitmap image = ((GraphicsShapeSet) shapeSet).getImage(shape);  //check if an image is registered for the body
        Bitmap image = null;
        
        UserData userdata = shape.getUserData();
        if (userdata != null && userdata instanceof UserImages)
        {
            image = ((UserImages) userdata).getImage();
        }
        
        if (image != null)
        { 
            //draw the image if one was found
            FXVector position = body.positionFX();
            matrix.reset();
            matrix.postRotate( ((float) body.rotation2FX() * 360f / FXUtil.TWO_PI_2FX),
                    image.getWidth() / 2, image.getHeight() / 2);
            matrix.postTranslate(
                    (position.xFX >> FXUtil.DECIMAL) - image.getWidth() / 2,
                    (position.yFX >> FXUtil.DECIMAL) - image.getHeight() / 2);
            Paint paint = renderBodies;
            if(! body.isDynamic())
            {
                paint = renderStaticBodies;
            }
            canvas.drawBitmap(image, matrix, paint);
        }
        else
        {
            FXVector[] positions = body.getVertices();
            if (positions.length == 1)
            {
                canvas.drawCircle(
                        body.positionFX().xFX >> FXUtil.DECIMAL, 
                        body.positionFX().yFX >> FXUtil.DECIMAL, 
                        body.shape().getBoundingRadiusFX() >> FXUtil.DECIMAL, 
                        renderBodies);
            }
            else
            {
                for( int j = positions.length - 1, i = 0; i < positions.length; j = i,i++)
                {           
                    canvas.drawLine(
                            positions[i].xFX >> FXUtil.DECIMAL, 
                            positions[i].yFX >> FXUtil.DECIMAL, 
                            positions[j].xFX >> FXUtil.DECIMAL, 
                            positions[j].yFX >> FXUtil.DECIMAL, 
                            renderBodies);
                }    
            }
        }
    }
   
    public void tick()
    {
        setGravity(newGravity);
        
        super.tick();
        
        if (bodyToRemove != null)
        {
            removeBody(bodyToRemove);
            bodyToRemove = null;
        }
    }
    
    public void scheduleBodyRemove(Body b)
    {
        bodyToRemove = b;
    }
        
    //Setter and Getters
    
    public boolean isDrawLandscape()
    {
        return drawLandscape;
    }

    public void setDrawLandscape(boolean drawLandscape)
    {
        this.drawLandscape = drawLandscape;
    }
   
    public void setNewGravity(FXVector newGravity)
    {
        //check for perfectly balanced phone
        if (newGravity.xFX == 0 && newGravity.yFX == 0)
        {
            return;
        }
        
        newGravity.normalize();
        
        newGravity.multFX(baseGravityFX);
        this.newGravity = newGravity;
    }
    
    public void onAccuracyChanged(int sensor, int accuracy)
    {        
    }

    public void onSensorChanged(int sensor, float[] values)
    {
        switch(sensor) 
        {        
	        case SensorManager.SENSOR_ACCELEROMETER:
	        	int X = (int)(values[0] * 100);
	        	int Y = (int)(values[1] * 100);
//	        	Random r = new Random();
//	        	X = r.nextInt(1000) - 500;
//	        	Y = 700;
	        	
	        	if (X > 500) X = 500;
	        	if (X < -500) X = -500;
	        	if (Y < 500) Y = 500;
	        	if (Y > -500) Y = -500;
	
	        	FXVector gravityRaw = new FXVector( X, - Y);
	            gravityRaw.normalize();
	            gravityRaw.multFX(baseGravityFX);
	
	            setNewGravity( gravityRaw );
	            
	            
	            Body head = null;
	            if ( ((UserImages)getBodies()[0].shape().getUserData()).type == 1)
		            head = getBodies()[0];
	            else
		            head = getBodies()[1];
	            
	            double angle = X * 45 / 500.0;
	            if (angle < 0) angle = 360 + angle;
	            
	            head.setRotationDeg((int)angle);
	            
	            break;
	        default: 
	            break;
        }
    }
    
}
